/**
 * Software License, Version 1.0
 * 
 * Copyright 2003 The Trustees of Indiana University.  All rights reserved.
 * 
 *
 *Redistribution and use in source and binary forms, with or without 
 *modification, are permitted provided that the following conditions are met:
 *
 *1) All redistributions of source code must retain the above copyright notice,
 * the list of authors in the original source code, this list of conditions and
 * the disclaimer listed in this license;
 *2) All redistributions in binary form must reproduce the above copyright 
 * notice, this list of conditions and the disclaimer listed in this license in
 * the documentation and/or other materials provided with the distribution;
 *3) Any documentation included with all redistributions must include the 
 * following acknowledgement:
 *
 *"This product includes software developed by the Community Grids Lab. For 
 * further information contact the Community Grids Lab at 
 * http://communitygrids.iu.edu/."
 *
 * Alternatively, this acknowledgement may appear in the software itself, and 
 * wherever such third-party acknowledgments normally appear.
 * 
 *4) The name Indiana University or Community Grids Lab or NaradaBrokering, 
 * shall not be used to endorse or promote products derived from this software 
 * without prior written permission from Indiana University.  For written 
 * permission, please contact the Advanced Research and Technology Institute 
 * ("ARTI") at 351 West 10th Street, Indianapolis, Indiana 46202.
 *5) Products derived from this software may not be called NaradaBrokering, 
 * nor may Indiana University or Community Grids Lab or NaradaBrokering appear
 * in their name, without prior written permission of ARTI.
 * 
 *
 * Indiana University provides no reassurances that the source code provided 
 * does not infringe the patent or any other intellectual property rights of 
 * any other entity.  Indiana University disclaims any liability to any 
 * recipient for claims brought by any other entity based on infringement of 
 * intellectual property rights or otherwise.  
 *
 *LICENSEE UNDERSTANDS THAT SOFTWARE IS PROVIDED "AS IS" FOR WHICH NO 
 *WARRANTIES AS TO CAPABILITIES OR ACCURACY ARE MADE. INDIANA UNIVERSITY GIVES
 *NO WARRANTIES AND MAKES NO REPRESENTATION THAT SOFTWARE IS FREE OF 
 *INFRINGEMENT OF THIRD PARTY PATENT, COPYRIGHT, OR OTHER PROPRIETARY RIGHTS. 
 *INDIANA UNIVERSITY MAKES NO WARRANTIES THAT SOFTWARE IS FREE FROM "BUGS", 
 *"VIRUSES", "TROJAN HORSES", "TRAP DOORS", "WORMS", OR OTHER HARMFUL CODE.  
 *LICENSEE ASSUMES THE ENTIRE RISK AS TO THE PERFORMANCE OF SOFTWARE AND/OR 
 *ASSOCIATED MATERIALS, AND TO THE PERFORMANCE AND VALIDITY OF INFORMATION 
 *GENERATED USING SOFTWARE.
 */
package cgl.narada.wsinfra;

import javax.xml.soap.SOAPMessage;

import cgl.narada.wsinfra.deployment.SOAPContext;
import cgl.narada.wsinfra.exception.DeploymentException;
import cgl.narada.wsinfra.exception.IncorrectExchangeException;
import cgl.narada.wsinfra.exception.MessageFlowException;
import cgl.narada.wsinfra.exception.ParsingException;
import cgl.narada.wsinfra.exception.ProcessingException;
import cgl.narada.wsinfra.exception.UnknownExchangeException;
import cgl.narada.wsinfra.exception.WsFaultException;
import cgl.narada.wsinfra.schemas.addressing.ActionDocument;
import cgl.narada.wsinfra.schemas.addressing.ToDocument;
import cgl.narada.wsinfra.schemas.soap.EnvelopeDocument;
import cgl.narada.wsinfra.util.ExceptionToFaultConversion;
import cgl.narada.wsinfra.util.SoapEnvelopeConversion;
import cgl.narada.wsinfra.util.SoapPrinter;
import cgl.narada.wsinfra.wsa.AddressingHeaders;
import cgl.narada.wsinfra.wsa.ParseWsaHeaders;
import cgl.narada.wsinfra.wsa.WsaActions;
import cgl.narada.wsinfra.wsa.WsaProcessingFactory;

/** All Ws specifications extend this processor. This processor provides the
    entry point to connect WsMessageFlow implementations with the actual
    processing of exchanges in various protocols.

    @author Shrideep Pallickara
    $Date: 2005/07/29 22:41:29 $
    $Revision: 1.6 $
*/

public abstract class WsProcessor {
  private SoapEnvelopeConversion soapEnvelopeConversion;
  private ExceptionToFaultConversion exceptionToFaultConversion;
  private ParseWsaHeaders parseWsaHeaders;
  private WsaActions wsaActions;

  private String moduleName = "WsProcessor: ";
  
  /** Sets the message flow which the processor should use. */
  public abstract void setMessageFlow(WsMessageFlow messageFlow) 
    throws DeploymentException;
  
  /** Gets the message flow which the processor should use. */
  public abstract WsMessageFlow getMessageFlow(); 
  
  
  /** Process the exchange. The argument also indicates the direction in
      which the exchange has actually traversed. */
  public boolean processExchange(SOAPContext soapContext, 
				 int direction) 
    throws UnknownExchangeException, IncorrectExchangeException, 
    MessageFlowException, ProcessingException {
    String errorReport = null;
    if (soapContext == null) {
      errorReport = "The specified SOAPContext is NULL.";
      throwProcessingException(errorReport);
    }
    
    if (!soapContext.encapsulatesSOAPMessage()) {
      errorReport = "Specified SOAPContext does not encapsulate SOAPMessage.";
    }
    SOAPMessage soapMessage = soapContext.getSOAPMessage();
    if (soapMessage == null) {
      errorReport = "The encapsulated SOAPMessage is NULL.";
    }

    if (errorReport != null) {
      throwProcessingException(errorReport);
    }

    SoapEnvelopeConversion soapEnvelopeConversion = 
      getSoapEnvelopeConversion();
    EnvelopeDocument envelopeDocument = 
      soapEnvelopeConversion.getEnvelopeDocument(soapMessage);
    boolean operationReturn = processExchange(envelopeDocument, direction);
    
    /** This accounts for the scenario where a SOAP message may have been
	modified. The new envelope document thus needs to converted to a
	SOAP message. */
    soapMessage = soapEnvelopeConversion.getSOAPMessage(envelopeDocument);
    soapContext.setSOAPMessage(soapMessage);
    return operationReturn;
  }

  
  private void throwProcessingException(String errorReport) 
  throws ProcessingException {
    ProcessingException pe = new ProcessingException(moduleName + errorReport);
    throw pe;
  }


  /** Check to see if the addressing headers indicate that this is a fault.
      If so, return true else return false. */
  public boolean isFault(AddressingHeaders addressingHeaders) {
    getWsaActions();
    if (addressingHeaders == null) {
      return false;
    }
    ActionDocument actionDocument = addressingHeaders.getAction();
    String action = null;
    if (actionDocument != null) {
      action = actionDocument.getAction().getStringValue();
      if (action.equals(wsaActions.getFault()) ) {
	return true;
      }
    }
    return false;
  }



  /** Process the exchange. The argument also indicates the direction in
      which the exchange has actually traversed. */
  public boolean processExchange(SOAPMessage soapMessage, 
				 int direction) 
    throws UnknownExchangeException, IncorrectExchangeException, 
    MessageFlowException, ProcessingException {
    SoapEnvelopeConversion soapEnvelopeConversion = 
      getSoapEnvelopeConversion();
    EnvelopeDocument envelopeDocument = 
      soapEnvelopeConversion.getEnvelopeDocument(soapMessage);
    boolean operationReturn = processExchange(envelopeDocument, direction);
    
    /** This accounts for the scenario where a SOAP message may have been
	modified. The new envelope document thus needs to converted to a
	SOAP message. */
    System.out.println(moduleName + "Modified SOAP message should be\n" +
		       envelopeDocument.toString() );
    soapMessage = soapEnvelopeConversion.getSOAPMessage(envelopeDocument);
    
    try {
      System.out.println(moduleName + "Modified SOAP message is \n" +
			 SoapPrinter.getStringRepresentation(soapMessage) );
    } catch (Exception e) {
      System.out.println(moduleName + "Problems printing SOAPMessage"); 
    }
    
    return operationReturn;
  }


  /** This is the method which all implementations of the various WS
      specifications will override. Note that deployement need only pass the
      JAVA-WORLD javax.xml.SOAPMessage and all the processing and conversions
      take place automatically. */
  public abstract boolean 
  processExchange(EnvelopeDocument envelopeDocument,
		  int direction)
    throws UnknownExchangeException, IncorrectExchangeException,
    ProcessingException, MessageFlowException;

  
  

  /** This method will convert the fault to an appropriate fault document
      and route it back either to the application or over the network. */
  public void manageFaultException(WsFaultException wsFaultException,
				   int direction) 
    throws MessageFlowException {
    exceptionToFaultConversion = getExceptionToFaultConversion();
    EnvelopeDocument faultDocument = 
      exceptionToFaultConversion.convertToSOAPFault(wsFaultException);

    if (direction == WsMessageFlow.FROM_NETWORK) {
      /** Will be routing Fault over the network */
      enrouteToNetwork(faultDocument);
      return;
    }
    
    if (direction == WsMessageFlow.FROM_APPLICATION) {
      /** Will be routing Fault to the application */
      enrouteToApplication(faultDocument);
      return;
    }
    
    String errorReport = moduleName + "Unknown direction type [" + direction 
      + "] has been specified for issuing the fault. ";
    throw new MessageFlowException(errorReport);
  }


  /** Creates a SOAP Message from the envelope document. */
  private SOAPMessage 
  getSOAPMessage(EnvelopeDocument envelopeDocument) 
    throws MessageFlowException {
    SoapEnvelopeConversion soapEnvelopeConversion = 
      getSoapEnvelopeConversion();
    SOAPMessage soapMessage = null;
    try {
      soapMessage =  soapEnvelopeConversion.getSOAPMessage(envelopeDocument);
    } catch (ProcessingException ex) {
      String errorReport ="Problems converting to javax.xml.SOAPMessage\n"
	+ ex.toString();
      throw new MessageFlowException(errorReport);
    }
    return soapMessage;
  }
  
  
  /** Retrieves the MessageFlow that should have been PREVIOUSLY registered.
      If there are problems, a messageflow exception is thrown. */
  private WsMessageFlow retrieveMessageFlow() throws MessageFlowException {
    String errorReport = null;
    WsMessageFlow wsMessageFlow = getMessageFlow();
    if (wsMessageFlow == null) {
      errorReport = moduleName + "The WsMessageFlow has NOT been set. Please"
	+ " remedy this situation by calling the setMessageFlow() method.";
      throw new MessageFlowException(errorReport);
    }
    return wsMessageFlow;
  }

  

  /** This method routes an envelope document enroute to the network. 
      Appropriate conversion to the SOAPMessage object is made, and the
      <i>enrouteToNetwork(SOAPMessage, sendTo)</i> method of the registered 
      WsMessageFlow is invoked. */
  public void 
  enrouteToNetwork(EnvelopeDocument envelopeDocument, String sendTo) 
    throws MessageFlowException {
    /**WsMessageFlow wsMessageFlow = retrieveMessageFlow();
       SOAPMessage soapMessage = getSOAPMessage(envelopeDocument);
       wsMessageFlow.enrouteToNetwork(soapMessage, sendTo);    */
  }
  
  
  
  /** This method routes an envelope document enroute to the network. The
      method first computes the destination <i>sendTo</i> based on the 
      [wsa:To] element. Then the appropriate conversion to the SOAPMessage 
      object is made, and the <i>enrouteToNetwork(SOAPMessage, sendTo)</i> 
      method of the registered WsMessageFlow is invoked.  */
  public void 
  enrouteToNetwork(EnvelopeDocument envelopeDocument) 
    throws MessageFlowException { 
    /**String sendTo = getSendTo(envelopeDocument);
       enrouteToNetwork(envelopeDocument, sendTo); */

    WsMessageFlow wsMessageFlow = retrieveMessageFlow();
    SOAPMessage soapMessage = getSOAPMessage(envelopeDocument);
    wsMessageFlow.enrouteToNetwork(soapMessage);
  }
  
  
  
  /** This method routes an envelope document enroute to the application. 
      Appropriate conversion to the SOAPMessage object is made, and the 
      <i>enrouteToApplication(SOAPMessage)</i> method of the registered 
      WsMessageFlow is invoked. */
  public void 
  enrouteToApplication(EnvelopeDocument envelopeDocument) 
    throws MessageFlowException {
    WsMessageFlow wsMessageFlow = retrieveMessageFlow();
    SOAPMessage soapMessage = getSOAPMessage(envelopeDocument);
    wsMessageFlow.enrouteToApplication(soapMessage);
  }

  
  
  /** This method parses the envelopeDocument to look for the [wsa:To] 
      element. If one exists this method retrives the address element 
      from it and returns it as a String, if NOT it will return null. */
  private String getSendTo(EnvelopeDocument envelopeDocument) {
    parseWsaHeaders = getParseWsaHeaders();
    String to = null;
    ToDocument toDocument = null;
    
    try {
      toDocument = parseWsaHeaders.getTo(envelopeDocument);
    } catch (ParsingException parseEx) {
      /** Ignore this exception. */
    }
    
    if (toDocument == null) {
      return to;
    }
    
    if (toDocument.getTo() == null) {
      return to;
    }
    
    to = toDocument.getTo().getStringValue();
    return to;
  }



  /** Checks to see if the ParseWsaHeaders is NULL. If so, the instance is
      retrieved and the initialized instance is passed back. */
  private ParseWsaHeaders getParseWsaHeaders() {
    if (parseWsaHeaders == null) {
      parseWsaHeaders = WsaProcessingFactory.getParseWsaHeaders();
    }
    return parseWsaHeaders;
  }
  
  
  /** Gets the soap envelope conversion instance. */
  private SoapEnvelopeConversion getSoapEnvelopeConversion() {
    if (soapEnvelopeConversion == null) {
      soapEnvelopeConversion = SoapEnvelopeConversion.getInstance();
    }
    return soapEnvelopeConversion;
  }
  


  /** Checks to see if exceptionToFaultConversion is NULL. If so, the 
      instance is retrieved and the initialized instance is passed back. */
  private ExceptionToFaultConversion getExceptionToFaultConversion() {
    if (exceptionToFaultConversion == null) {
      exceptionToFaultConversion = ExceptionToFaultConversion.getInstance();
    }
    return exceptionToFaultConversion;
  }
  
  private WsaActions getWsaActions() {
    if (wsaActions == null) {
      wsaActions = WsaProcessingFactory.getWsaActions();
    }
    return wsaActions;
  }
  
}
